mirror of
https://github.com/writeas/writefreely-swiftui-multiplatform.git
synced 2024-11-15 01:11:02 +00:00
Write logs to new post (#236)
This commit is contained in:
parent
fcd7c167c7
commit
1361ff7586
@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- [Mac] Added a menu item for toggling the toolbar.
|
||||
- [Mac] In a post with unpublished changes (i.e., with "local" or "edited" status), the post is autosaved after a one-second pause in typing.
|
||||
- [iOS/Mac] Added a context-menu item to delete local posts from the post list.
|
||||
- [iOS/Mac] Added methods to fetch device logs.
|
||||
- [iOS] Added a settings option to generate a log file as a new local draft (iOS 15+).
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -1,9 +1,4 @@
|
||||
//
|
||||
// Logging.swift
|
||||
// WriteFreely-MultiPlatform
|
||||
//
|
||||
// Created by Angelo Stavrow on 2022-06-25.
|
||||
//
|
||||
// Credit for much of this class: https://steipete.com/posts/logging-in-swift/
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
@ -14,6 +9,11 @@ protocol LogWriter {
|
||||
func logCrashAndSetFlag(error: Error)
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
protocol LogReader {
|
||||
func fetchLogs() -> [String]
|
||||
}
|
||||
|
||||
final class Logging {
|
||||
|
||||
private let logger: Logger
|
||||
@ -48,3 +48,57 @@ extension Logging: LogWriter {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Logging: LogReader {
|
||||
|
||||
@available(iOS 15, *)
|
||||
func fetchLogs() -> [String] {
|
||||
var logs: [String] = []
|
||||
|
||||
do {
|
||||
let osLog = try getLogEntries()
|
||||
for logEntry in osLog {
|
||||
let formattedEntry = formatEntry(logEntry)
|
||||
logs.append(formattedEntry)
|
||||
}
|
||||
} catch {
|
||||
logs.append("Could not fetch logs")
|
||||
}
|
||||
|
||||
return logs
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
private func getLogEntries() throws -> [OSLogEntryLog] {
|
||||
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
|
||||
let allEntries = try Array(logStore.__entriesEnumerator(position: oneHourAgo, predicate: nil))
|
||||
return allEntries
|
||||
.compactMap { $0 as? OSLogEntryLog }
|
||||
.filter { $0.subsystem == subsystem }
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
private func formatEntry(_ logEntry: OSLogEntryLog) -> String {
|
||||
/// The desired format is:
|
||||
/// `date [process/category] LEVEL: composedMessage (threadIdentifier)`
|
||||
var level: String = ""
|
||||
switch logEntry.level {
|
||||
case .debug:
|
||||
level = "DEBUG"
|
||||
case .info:
|
||||
level = "INFO"
|
||||
case .notice:
|
||||
level = "NOTICE"
|
||||
case .error:
|
||||
level = "ERROR"
|
||||
case .fault:
|
||||
level = "FAULT"
|
||||
default:
|
||||
level = "UNDEFINED"
|
||||
}
|
||||
// swiftlint:disable:next line_length
|
||||
return "\(logEntry.date) [\(logEntry.process)/\(logEntry.category)] \(level): \(logEntry.composedMessage) (\(logEntry.threadIdentifier))"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ struct CheckForDebugModifier {
|
||||
struct WriteFreely_MultiPlatformApp: App {
|
||||
@StateObject private var model = WriteFreelyModel.shared
|
||||
|
||||
private let logger = Logging(for: String(describing: WriteFreely_MultiPlatformApp.self))
|
||||
|
||||
#if os(macOS)
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@StateObject var updaterViewModel = MacUpdatesViewModel()
|
||||
@ -68,6 +70,11 @@ struct WriteFreely_MultiPlatformApp: App {
|
||||
)
|
||||
)
|
||||
}
|
||||
.onAppear {
|
||||
if #available(iOS 15, *) {
|
||||
if didCrash { generateCrashLogPost() }
|
||||
}
|
||||
}
|
||||
.withErrorHandling()
|
||||
.environmentObject(model)
|
||||
.environment(\.managedObjectContext, LocalStorageManager.standard.container.viewContext)
|
||||
@ -167,6 +174,33 @@ struct WriteFreely_MultiPlatformApp: App {
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
private func generateCrashLogPost() {
|
||||
logger.log("Generating local log post...")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||
// Unset selected post and collection and navigate to local drafts.
|
||||
self.model.selectedPost = nil
|
||||
self.model.selectedCollection = nil
|
||||
self.model.showAllPosts = false
|
||||
|
||||
// Create the new log post.
|
||||
let newLogPost = model.editor.generateNewLocalPost(withFont: 2)
|
||||
newLogPost.title = "Logs For Support"
|
||||
var postBody: [String] = [
|
||||
"WriteFreely-Multiplatform v\(Bundle.main.appMarketingVersion) (\(Bundle.main.appBuildVersion))",
|
||||
"Generated \(Date())",
|
||||
""
|
||||
]
|
||||
postBody.append(contentsOf: logger.fetchLogs())
|
||||
newLogPost.body = postBody.joined(separator: "\n")
|
||||
|
||||
self.model.selectedPost = newLogPost
|
||||
}
|
||||
|
||||
logger.log("Generated local log post.")
|
||||
}
|
||||
|
||||
private func resetCrashFlags() {
|
||||
UserDefaults.shared.set(false, forKey: WFDefaults.didHaveFatalError)
|
||||
UserDefaults.shared.removeObject(forKey: WFDefaults.fatalErrorDescription)
|
||||
|
@ -1050,7 +1050,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_ENTITLEMENTS = "ActionExtension-iOS/ActionExtension-iOS.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 690;
|
||||
CURRENT_PROJECT_VERSION = 691;
|
||||
DEVELOPMENT_TEAM = TPPAB4YBA6;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "ActionExtension-iOS/Info.plist";
|
||||
@ -1081,7 +1081,7 @@
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_ENTITLEMENTS = "ActionExtension-iOS/ActionExtension-iOS.entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 690;
|
||||
CURRENT_PROJECT_VERSION = 691;
|
||||
DEVELOPMENT_TEAM = TPPAB4YBA6;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "ActionExtension-iOS/Info.plist";
|
||||
@ -1224,7 +1224,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 690;
|
||||
CURRENT_PROJECT_VERSION = 691;
|
||||
DEVELOPMENT_TEAM = TPPAB4YBA6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = iOS/Info.plist;
|
||||
@ -1250,7 +1250,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 690;
|
||||
CURRENT_PROJECT_VERSION = 691;
|
||||
DEVELOPMENT_TEAM = TPPAB4YBA6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = iOS/Info.plist;
|
||||
|
@ -1,7 +1,11 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
|
||||
@EnvironmentObject var model: WriteFreelyModel
|
||||
@State private var isShowingAlert = false
|
||||
|
||||
private let logger = Logging(for: String(describing: SettingsView.self))
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
@ -14,21 +18,22 @@ struct SettingsView: View {
|
||||
Section(header: Text("Appearance")) {
|
||||
PreferencesView(preferences: model.preferences)
|
||||
}
|
||||
Section(header: Text("External Links")) {
|
||||
HStack {
|
||||
Spacer()
|
||||
Link("View the Guide", destination: model.howToURL)
|
||||
Spacer()
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Link("Visit the Help Forum", destination: model.helpURL)
|
||||
Spacer()
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Link("Write a Review on the App Store", destination: model.reviewURL)
|
||||
Spacer()
|
||||
Section(header: Text("Help and Support")) {
|
||||
Link("View the Guide", destination: model.howToURL)
|
||||
Link("Visit the Help Forum", destination: model.helpURL)
|
||||
Link("Write a Review on the App Store", destination: model.reviewURL)
|
||||
if #available(iOS 15.0, *) {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Button(
|
||||
action: didTapGenerateLogPostButton,
|
||||
label: {
|
||||
Text("Create Log Post")
|
||||
}
|
||||
)
|
||||
Text("Generates a local post using recent logs. You can share this for troubleshooting.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
Section(header: Text("Acknowledgements")) {
|
||||
@ -55,8 +60,41 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $isShowingAlert) {
|
||||
Alert(
|
||||
title: Text("Log Post Created"),
|
||||
message: Text("Check your local drafts for app logs from the past 24 hours.")
|
||||
)
|
||||
}
|
||||
// .preferredColorScheme(preferences.selectedColorScheme) // See PreferencesModel for info.
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
private func didTapGenerateLogPostButton() {
|
||||
logger.log("Generating local log post...")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||
// Unset selected post and collection and navigate to local drafts.
|
||||
self.model.selectedPost = nil
|
||||
self.model.selectedCollection = nil
|
||||
self.model.showAllPosts = false
|
||||
|
||||
// Create the new log post.
|
||||
let newLogPost = model.editor.generateNewLocalPost(withFont: 2)
|
||||
newLogPost.title = "Logs For Support"
|
||||
var postBody: [String] = [
|
||||
"WriteFreely-Multiplatform v\(Bundle.main.appMarketingVersion) (\(Bundle.main.appBuildVersion))",
|
||||
"Generated \(Date())",
|
||||
""
|
||||
]
|
||||
postBody.append(contentsOf: logger.fetchLogs())
|
||||
newLogPost.body = postBody.joined(separator: "\n")
|
||||
|
||||
self.isShowingAlert = true
|
||||
}
|
||||
|
||||
logger.log("Generated local log post.")
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsView_Previews: PreviewProvider {
|
||||
|
Loading…
Reference in New Issue
Block a user