Download Sources/PromptGarageCore/Logging/ActivityLogger.swift from smlflg/PromtGarageMac: direct link, hf CLI and curl.
- Browser
- Download file 3.41 kB
-
https://huggingface.co/smlflg/PromtGarageMac/resolve/main/Sources/PromptGarageCore/Logging/ActivityLogger.swift
- Command line
-
hf download hf://smlflg/PromtGarageMac/Sources/PromptGarageCore/Logging/ActivityLogger.swift
-
curl -L -o ActivityLogger.swift https://huggingface.co/smlflg/PromtGarageMac/resolve/main/Sources/PromptGarageCore/Logging/ActivityLogger.swift
3.41 kB
| import Foundation | |
| /// Append-only audit log for user-visible mutations. Entries intentionally do | |
| /// not contain prompt titles, contents, tags, complete source identifiers, or | |
| /// selected file paths. | |
| public final class ActivityLogger: @unchecked Sendable { | |
| public let logURL: URL | |
| private let lock = NSLock() | |
| public init(logURL: URL) throws { | |
| self.logURL = logURL | |
| let fileManager = FileManager.default | |
| try fileManager.createDirectory( | |
| at: logURL.deletingLastPathComponent(), | |
| withIntermediateDirectories: true | |
| ) | |
| if !fileManager.fileExists(atPath: logURL.path) { | |
| guard fileManager.createFile(atPath: logURL.path, contents: nil) else { | |
| throw ActivityLoggerError.couldNotCreateLog(logURL) | |
| } | |
| } | |
| // The log is local operational metadata and should not be readable by | |
| // other accounts on the Mac. | |
| try fileManager.setAttributes( | |
| [.posixPermissions: 0o600], | |
| ofItemAtPath: logURL.path | |
| ) | |
| } | |
| public func logImport( | |
| statistics: ImportStatistics, | |
| sourceFamilies: [String] | |
| ) throws { | |
| try append(ActivityLogEntry( | |
| event: .importCompleted, | |
| promptID: nil, | |
| sourceFamily: nil, | |
| sourceFamilies: Array(Set(sourceFamilies)).sorted(), | |
| statistics: statistics | |
| )) | |
| } | |
| public func logImport(_ report: ImportReport) throws { | |
| // `sourceName` is deliberately omitted: even a last path component can | |
| // disclose personal project or export names. | |
| try append(ActivityLogEntry( | |
| event: .importCompleted, | |
| promptID: nil, | |
| sourceFamily: report.provider, | |
| sourceFamilies: [], | |
| statistics: report.statistics | |
| )) | |
| } | |
| public func logDeletion(promptID: Int64, sourceFamily: String?) throws { | |
| try append(ActivityLogEntry( | |
| event: .promptDeleted, | |
| promptID: promptID, | |
| sourceFamily: sourceFamily, | |
| sourceFamilies: [], | |
| statistics: nil | |
| )) | |
| } | |
| private func append(_ entry: ActivityLogEntry) throws { | |
| lock.lock() | |
| defer { lock.unlock() } | |
| let encoder = JSONEncoder() | |
| encoder.dateEncodingStrategy = .iso8601 | |
| encoder.outputFormatting = [.sortedKeys, .withoutEscapingSlashes] | |
| var line = try encoder.encode(entry) | |
| line.append(0x0A) | |
| let handle = try FileHandle(forWritingTo: logURL) | |
| defer { try? handle.close() } | |
| try handle.seekToEnd() | |
| try handle.write(contentsOf: line) | |
| try handle.synchronize() | |
| } | |
| } | |
| public enum ActivityLoggerError: LocalizedError, Sendable { | |
| case couldNotCreateLog(URL) | |
| public var errorDescription: String? { | |
| switch self { | |
| case let .couldNotCreateLog(url): | |
| "Das lokale Aktivitätsprotokoll konnte nicht angelegt werden: \(url.path)" | |
| } | |
| } | |
| } | |
| private struct ActivityLogEntry: Encodable { | |
| enum Event: String, Encodable { | |
| case importCompleted = "import_completed" | |
| case promptDeleted = "prompt_deleted" | |
| } | |
| let schemaVersion = 1 | |
| let timestamp = Date() | |
| let event: Event | |
| let promptID: Int64? | |
| let sourceFamily: String? | |
| let sourceFamilies: [String] | |
| let statistics: ImportStatistics? | |
| } | |