Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log viewer #851

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions OpenHABCore/Sources/OpenHABCore/Util/Logger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) 2010-2024 Contributors to the openHAB project
//
// See the NOTICE file(s) distributed with this work for additional
// information.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0
//
// SPDX-License-Identifier: EPL-2.0

import CoreTransferable
import OSLog

// Thanks to https://useyourloaf.com/blog/fetching-oslog-messages-in-swift/

// swiftlint:disable:next file_types_order
private extension OSLogEntryLog.Level {
var description: String {
switch self {
case .undefined: "undefined"
case .debug: "debug"
case .info: "info"
case .notice: "notice"
case .error: "error"
case .fault: "fault"
@unknown default: "default"
}
}
}

public extension Logger {
static func fetch(since date: Date,
predicateFormat: String) async throws -> [String] {
let store = try OSLogStore(scope: .currentProcessIdentifier)
let position = store.position(date: date)
let predicate = NSPredicate(format: predicateFormat)

let entries = try store
.getEntries(
at: position,
matching: predicate
)

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

var logs: [String] = []
for entry in entries {
try Task.checkCancellation()
if let log = entry as? OSLogEntryLog {
var attributedMessage = AttributedString(dateFormatter.string(from: entry.date))
attributedMessage.font = .headline

logs.append("""
\(entry.date.formatted(.iso8601)): \
\(log.category):\(log.level.description): \
\(entry.composedMessage)\n
""")
} else {
logs.append("\(entry.date): \(entry.composedMessage)\n")
}
}

if logs.isEmpty { logs = ["Nothing found"] }
return logs
}
}

public protocol LogServiceProtocol {
func fetchLogs(with template: NSPredicate) async -> String
}

public struct LogService {
public init() {}
}

extension LogService: LogServiceProtocol {
public func fetchLogs(with template: NSPredicate) async -> String {
let calendar = Calendar.current
guard let hourAgo = calendar.date(
byAdding: .hour,
value: -1,
to: Date.now
) else {
return "Invalid calendar"
}

do {
let predicate = template.withSubstitutionVariables(
[
"PREFIX": "org.openhab"
])

let logs = try await Logger.fetch(
since: hourAgo,
predicateFormat: predicate.predicateFormat
)
return logs.joined()
} catch {
return error.localizedDescription
}
}
}

// extension LogService: Transferable {
//
// static var containerUrl = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
//
// public static var transferRepresentation: some TransferRepresentation {
// FileRepresentation(exportedContentType: .commaSeparatedText) { csvFile in
// SentTransferredFile(csvFile.url)
// }
// }
// }
34 changes: 30 additions & 4 deletions openHAB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
DA0F37D023D4ACC7007EAB48 /* SliderRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0F37CF23D4ACC7007EAB48 /* SliderRow.swift */; };
DA15BFBD23C6726400BD8ADA /* ObservableOpenHABDataObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA15BFBC23C6726400BD8ADA /* ObservableOpenHABDataObject.swift */; };
DA162BEC2CD3B53E0040DAE5 /* LogsViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA162BEB2CD3B53E0040DAE5 /* LogsViewer.swift */; };
DA162BF02CD4CC730040DAE5 /* LogsViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA162BEF2CD4CC730040DAE5 /* LogsViewer.swift */; };
DA19E25B22FD801D002F8F2F /* OpenHABGeneralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA19E25A22FD801D002F8F2F /* OpenHABGeneralTests.swift */; };
DA21EAE22339621C001AB415 /* Throttler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA21EAE12339621C001AB415 /* Throttler.swift */; };
DA242C622C83588600AFB10D /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA242C612C83588600AFB10D /* SettingsView.swift */; };
Expand Down Expand Up @@ -110,13 +111,14 @@
DA7E1E4B2233986E002AEFD8 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7E1E47222EB00B002AEFD8 /* PlayerView.swift */; };
DA817E7A234BF39B00C91824 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = DA817E79234BF39B00C91824 /* CHANGELOG.md */; };
DA88F8C622EC377200B408E5 /* ReleaseNotes.md in Resources */ = {isa = PBXBuildFile; fileRef = DA88F8C522EC377100B408E5 /* ReleaseNotes.md */; };
DA8F986B2CDA4CAA001F5E8A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8F986A2CDA4CAA001F5E8A /* ContentView.swift */; };
DA9721C324E29A8F0092CCFD /* UserDefaultsBacked.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9721C224E29A8F0092CCFD /* UserDefaultsBacked.swift */; };
DA9F81872C85020F00B47B72 /* RTFTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9F81862C85020F00B47B72 /* RTFTextView.swift */; };
DAA070932B5181210060BB0E /* OpenHABImageDownloaderOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAA070922B5181210060BB0E /* OpenHABImageDownloaderOperation.swift */; };
DAA42BA821DC97E000244B2A /* NotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAA42BA721DC97DF00244B2A /* NotificationTableViewCell.swift */; };
DAA42BAA21DC983B00244B2A /* VideoUITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAA42BA921DC983B00244B2A /* VideoUITableViewCell.swift */; };
DAA42BAC21DC984A00244B2A /* WebUITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAA42BAB21DC984A00244B2A /* WebUITableViewCell.swift */; };
DAAC30872CBBF0420041927F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0775262346705F0086C685 /* ContentView.swift */; };
DAAC30872CBBF0420041927F /* SitemapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0775262346705F0086C685 /* SitemapView.swift */; };
DAC65FC7236EDF3900F4501E /* SpinnerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC65FC6236EDF3900F4501E /* SpinnerViewController.swift */; };
DAC6608D236F771600F4501E /* PreferencesSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC6608C236F771600F4501E /* PreferencesSwiftUIView.swift */; };
DAC9395522B00E7600C5F423 /* XCTestCaseExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC9395422B00E7600C5F423 /* XCTestCaseExtension.swift */; };
Expand Down Expand Up @@ -336,7 +338,7 @@
DA0775152346705D0086C685 /* openHABWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = openHABWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
DA07751A2346705F0086C685 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DA07751C2346705F0086C685 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DA0775262346705F0086C685 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
DA0775262346705F0086C685 /* SitemapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitemapView.swift; sourceTree = "<group>"; };
DA07752A2346705F0086C685 /* OpenHABWatchAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABWatchAppDelegate.swift; sourceTree = "<group>"; };
DA07752C2346705F0086C685 /* NotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = "<group>"; };
DA07752E2346705F0086C685 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
Expand All @@ -348,6 +350,7 @@
DA0F37CF23D4ACC7007EAB48 /* SliderRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderRow.swift; sourceTree = "<group>"; };
DA15BFBC23C6726400BD8ADA /* ObservableOpenHABDataObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableOpenHABDataObject.swift; sourceTree = "<group>"; };
DA162BEB2CD3B53E0040DAE5 /* LogsViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsViewer.swift; sourceTree = "<group>"; };
DA162BEF2CD4CC730040DAE5 /* LogsViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsViewer.swift; sourceTree = "<group>"; };
DA19E25A22FD801D002F8F2F /* OpenHABGeneralTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABGeneralTests.swift; sourceTree = "<group>"; };
DA1C2E4B230DC28F00FACFB0 /* Appfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Appfile; sourceTree = "<group>"; };
DA1C2E4C230DC28F00FACFB0 /* SnapshotHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -408,6 +411,7 @@
DA7E1E47222EB00B002AEFD8 /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
DA817E79234BF39B00C91824 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
DA88F8C522EC377100B408E5 /* ReleaseNotes.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = ReleaseNotes.md; sourceTree = "<group>"; };
DA8F986A2CDA4CAA001F5E8A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
DA9721C224E29A8F0092CCFD /* UserDefaultsBacked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsBacked.swift; sourceTree = "<group>"; };
DA9F81862C85020F00B47B72 /* RTFTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTFTextView.swift; sourceTree = "<group>"; };
DAA070922B5181210060BB0E /* OpenHABImageDownloaderOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHABImageDownloaderOperation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -666,6 +670,7 @@
isa = PBXGroup;
children = (
DAD0855F2AE47824001D36BE /* OpenHABWatch.swift */,
DA8F986A2CDA4CAA001F5E8A /* ContentView.swift */,
DA0775252346705F0086C685 /* Extension */,
1224F7C9228A8ED100750965 /* External */,
1224F7C8228A8EC600750965 /* Domain */,
Expand Down Expand Up @@ -776,7 +781,7 @@
DA658720236F841F007E2E7F /* Views */ = {
isa = PBXGroup;
children = (
DA0775262346705F0086C685 /* ContentView.swift */,
DA0775262346705F0086C685 /* SitemapView.swift */,
DA162BEB2CD3B53E0040DAE5 /* LogsViewer.swift */,
DAC6608C236F771600F4501E /* PreferencesSwiftUIView.swift */,
DAF457A323DB7A820018B495 /* Rows */,
Expand Down Expand Up @@ -871,6 +876,7 @@
653B54BF285C0AC700298ECD /* OpenHABRootViewController.swift */,
65570A7C2476D16A00D524EA /* OpenHABWebViewController.swift */,
DFB2624318830A3600D3244D /* OpenHABSitemapViewController.swift */,
DA162BEF2CD4CC730040DAE5 /* LogsViewer.swift */,
DAC65FC6236EDF3900F4501E /* SpinnerViewController.swift */,
DA6B2EF62C8B92E800DF77CF /* SelectionView.swift */,
DA242C612C83588600AFB10D /* SettingsView.swift */,
Expand Down Expand Up @@ -1093,6 +1099,7 @@
39C91164B60A5677322E8DE2 /* Frameworks */,
DA07751D2346705F0086C685 /* Sources */,
93F38C61238034AE001B1451 /* Embed Frameworks */,
DA162BEE2CD3EDFE0040DAE5 /* ShellScript */,
);
buildRules = (
);
Expand Down Expand Up @@ -1397,6 +1404,23 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
DA162BEE2CD3EDFE0040DAE5 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cd BuildTools\nSDKROOT=(xcrun --sdk macosx --show-sdk-path)\n\nswift package plugin --allow-writing-to-package-directory --allow-writing-to-directory \"$SRCROOT\" swiftformat \"$SRCROOT\" --config ./.swiftformat --cache /private/tmp/\nswift package plugin --allow-writing-to-package-directory --allow-writing-to-directory ../ swiftlint --cache-path /private/tmp/\n# Type a script or drag a script file from your workspace to insert its path.\n";
};
DAF0A2902C56FE9F00A14A6A /* Run swiftformat & swiftlint */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down Expand Up @@ -1480,8 +1504,9 @@
9350F17A23814FAC00054BA8 /* ObservableOpenHABSitemapPage.swift in Sources */,
DAF457A023DA3E1C0018B495 /* SegmentRow.swift in Sources */,
DAF4578923D79AA50018B495 /* DetailTextLabelView.swift in Sources */,
DAAC30872CBBF0420041927F /* ContentView.swift in Sources */,
DAAC30872CBBF0420041927F /* SitemapView.swift in Sources */,
DA15BFBD23C6726400BD8ADA /* ObservableOpenHABDataObject.swift in Sources */,
DA8F986B2CDA4CAA001F5E8A /* ContentView.swift in Sources */,
DAC9AF4924F966FA006DAE93 /* LazyView.swift in Sources */,
DA0776F0234788010086C685 /* UserData.swift in Sources */,
DAC6608D236F771600F4501E /* PreferencesSwiftUIView.swift in Sources */,
Expand Down Expand Up @@ -1540,6 +1565,7 @@
1224F78F228A89FD00750965 /* WatchMessageService.swift in Sources */,
DAA42BAC21DC984A00244B2A /* WebUITableViewCell.swift in Sources */,
DF4B84131886DAC400F34902 /* FrameUITableViewCell.swift in Sources */,
DA162BF02CD4CC730040DAE5 /* LogsViewer.swift in Sources */,
DF4B84161886EACA00F34902 /* GenericUITableViewCell.swift in Sources */,
935B484625342B8E00E44CF0 /* URL+Static.swift in Sources */,
B7D5ECE121499E55001B0EC6 /* MapViewTableViewCell.swift in Sources */,
Expand Down
87 changes: 87 additions & 0 deletions openHAB/LogsViewer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) 2010-2024 Contributors to the openHAB project
//
// See the NOTICE file(s) distributed with this work for additional
// information.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0
//
// SPDX-License-Identifier: EPL-2.0

// import SwiftUI
//
// struct LogView: View {
// let logs: [OSLogEntryLog]
//
// var body: some View {
// List(logs, id: \.self) { log in
// VStack(alignment: .leading) {
// Text(log.composedMessage)
// HStack {
// Text(log.subsystem)
// Text(log.date, format: .dateTime)
// }.bold()
// }
// }
// }
// }
//
// #Preview {
// LogView(logs: .init([]))
// }

import Foundation
import OpenHABCore
import OSLog
import SwiftUI

// swiftlint:disable:next file_types_order
struct LogsViewer: View {
let template = NSPredicate(
format: "(subsystem BEGINSWITH $PREFIX)"
)
let myFont = Font
.system(size: 10)
.monospaced()

var logService: LogServiceProtocol

var body: some View {
List {
Text(text)
.font(myFont)
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
ShareLink(
item: text,
preview: SharePreview("Logs", image: Image(.openHABIcon))
) {
Label("Share Logs", systemSymbol: .squareAndArrowUp)
}
}
}
.navigationTitle("Logs")
.task {
text = await logService.fetchLogs(with: template)
}
}

@State private var text = "Loading..."
}

#if DEBUG
struct MockLogService: LogServiceProtocol {
func fetchLogs(with template: NSPredicate) async -> String {
"""
Mocked Data
Test data
"""
}
}
#endif

#Preview {
LogsViewer(logService: MockLogService())
}
10 changes: 10 additions & 0 deletions openHAB/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct SettingsView: View {

@State private var sitemaps: [OpenHABSitemap] = []

@State private var exportShown = false

@Environment(\.dismiss) private var dismiss

var appData: OpenHABDataObject? {
Expand Down Expand Up @@ -287,6 +289,14 @@ struct SettingsView: View {
}
}

Section(header: Text(LocalizedStringKey("debug"))) {
NavigationLink {
LogsViewer(logService: LogService())
} label: {
Text("Logs")
}
}

Section(header: Text(LocalizedStringKey("about_settings"))) {
LabeledContent("App Version", value: appVersion)

Expand Down
Loading