Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,18 @@ public actor FileProviderLog: FileProviderLogging {
let newFile = logsDirectory.appendingPathComponent(name, isDirectory: false)

if fileManager.createFile(atPath: newFile.path, contents: nil) == false {
logger.error("Failed to create new log file at: \"\(newFile.path)\".")
logger.error("Failed to create new log file at: \"\(newFile.path, privacy: .public)\".")
return
} else {
logger.debug("Created new log file at: \"\(newFile.path)\".")
logger.debug("Created new log file at: \"\(newFile.path, privacy: .public)\".")
}

do {
file = newFile
handle = try FileHandle(forWritingTo: newFile)
logger.debug("Opened new log file for writing at: \"\(newFile.path)\".")
logger.debug("Opened new log file for writing at: \"\(newFile.path, privacy: .public)\".")
} catch {
logger.error("Failed to open new log file at \"\(newFile.path)\" for writing: \(error.localizedDescription, privacy: .public)")
logger.error("Failed to open new log file at \"\(newFile.path, privacy: .public)\" for writing: \(error.localizedDescription, privacy: .public)")
}

// Clean up old log files (older than 24 hours)
Expand Down Expand Up @@ -212,9 +212,9 @@ public actor FileProviderLog: FileProviderLogging {
}
}

private func writeToUnifiedLoggingSystem(level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?]) {
private func writeToUnifiedLoggingSystem(level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?], file: StaticString, function: StaticString, line: UInt) {
if details.isEmpty {
logger.log(level: level, "\(message, privacy: .public)")
logger.log(level: level, "\(message, privacy: .public)\n\n\(file, privacy: .public):\(line, privacy: .public) \(function, privacy: .public)")
return
}

Expand Down Expand Up @@ -249,13 +249,13 @@ public actor FileProviderLog: FileProviderLogging {
return "- \(key.rawValue): \(valueDescription)"
}

logger.log(level: level, "\(message, privacy: .public)\n\n\(detailDescriptions.joined(separator: "\n"), privacy: .public)")
logger.log(level: level, "\(message, privacy: .public)\n\n\(detailDescriptions.joined(separator: "\n"), privacy: .public)\n\n\(file, privacy: .public):\(line, privacy: .public) \(function, privacy: .public)")
}

public func write(category: String, level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?]) {
public func write(category: String, level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?], file: StaticString, function: StaticString, line: UInt) {
#if DEBUG

writeToUnifiedLoggingSystem(level: level, message: message, details: details)
writeToUnifiedLoggingSystem(level: level, message: message, details: details, file: file, function: function, line: line)

#else

Expand Down Expand Up @@ -288,7 +288,7 @@ public actor FileProviderLog: FileProviderLogging {

let date = Date()
let formattedDate = messageDateFormatter.string(from: date)
let entry = FileProviderLogMessage(category: category, date: formattedDate, details: details, level: levelDescription, message: message)
let entry = FileProviderLogMessage(category: category, date: formattedDate, details: details, level: levelDescription, message: message, file: file, function: function, line: line)

do {
let object = try encoder.encode(entry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import Foundation
/// A data model for the rich JSON object to be written into the JSON lines log files.
///
public struct FileProviderLogMessage: Encodable {
enum CodingKeys: CodingKey {
case category
case date
case details
case file
case function
case level
case line
case message
}

///
/// As used with `Logger` of the `os` framework.
///
Expand All @@ -25,11 +36,26 @@ public struct FileProviderLogMessage: Encodable {
///
public let details: [String: FileProviderLogDetail?]

///
/// The source code file which generates the log message.
///
public let file: String?

///
/// The calling function generating this message.
///
public let function: String?

///
/// Textual representation of the associated `OSLogType`.
///
public let level: String

///
/// The line in the source code file which generates this message.
///
public let line: UInt?

///
/// The actual text for the entry.
///
Expand All @@ -38,7 +64,7 @@ public struct FileProviderLogMessage: Encodable {
///
/// Custom initializer to support arbitrary types as detail values.
///
init(category: String, date: String, details: [FileProviderLogDetailKey: Any?], level: String, message: String) {
init(category: String, date: String, details: [FileProviderLogDetailKey: Any?], level: String, message: String, file: StaticString, function: StaticString, line: UInt) {
self.category = category
self.date = date

Expand All @@ -51,5 +77,30 @@ public struct FileProviderLogMessage: Encodable {
self.details = transformedDetails
self.level = level
self.message = message

#if DEBUG
self.file = String("\(file)")
self.function = String("\(function)")
self.line = line
#else
self.file = nil
self.function = nil
self.line = nil
#endif
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(category, forKey: .category)
try container.encode(date, forKey: .date)
try container.encode(details, forKey: .details)
try container.encode(level, forKey: .level)
try container.encode(message, forKey: .message)

#if DEBUG
try container.encode(file, forKey: .file)
try container.encode(function, forKey: .function)
try container.encode(line, forKey: .line)
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,51 +37,63 @@ public struct FileProviderLogger: Sendable {
/// Dispatch a task to write a message with the level `OSLogType.debug`.
///
/// - Parameters:
/// - message: The main text message of the entry in the logs.
/// - details: Additional contextual data.
/// - message: A human-readable message, preferably generic and without interpolations. The `details` argument is for arguments.
/// - details: Structured and contextual details about a message.
/// - file: Implementations should have `#filePath` as the default value for this.
/// - function: Implementations should have `#function` as the default value for this.
/// - line: Implementations should have `#line` as the default value for this.
///
public func debug(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:]) {
public func debug(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:], file: StaticString = #filePath, function: StaticString = #function, line: UInt = #line) {
Task {
await log.write(category: category, level: .debug, message: message, details: details)
await log.write(category: category, level: .debug, message: message, details: details, file: file, function: function, line: line)
}
}

///
/// Dispatch a task to write a message with the level `OSLogType.info`.
///
/// - Parameters:
/// - message: The main text message of the entry in the logs.
/// - details: Additional contextual data.
/// - message: A human-readable message, preferably generic and without interpolations. The `details` argument is for arguments.
/// - details: Structured and contextual details about a message.
/// - file: Implementations should have `#filePath` as the default value for this.
/// - function: Implementations should have `#function` as the default value for this.
/// - line: Implementations should have `#line` as the default value for this.
///
public func info(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:]) {
public func info(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:], file: StaticString = #filePath, function: StaticString = #function, line: UInt = #line) {
Task {
await log.write(category: category, level: .info, message: message, details: details)
await log.write(category: category, level: .info, message: message, details: details, file: file, function: function, line: line)
}
}

///
/// Dispatch a task to write a message with the level `OSLogType.error`.
///
/// - Parameters:
/// - message: The main text message of the entry in the logs.
/// - details: Additional contextual data.
/// - message: A human-readable message, preferably generic and without interpolations. The `details` argument is for arguments.
/// - details: Structured and contextual details about a message.
/// - file: Implementations should have `#filePath` as the default value for this.
/// - function: Implementations should have `#function` as the default value for this.
/// - line: Implementations should have `#line` as the default value for this.
///
public func error(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:]) {
public func error(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:], file: StaticString = #filePath, function: StaticString = #function, line: UInt = #line) {
Task {
await log.write(category: category, level: .error, message: message, details: details)
await log.write(category: category, level: .error, message: message, details: details, file: file, function: function, line: line)
}
}

///
/// Dispatch a task to write a message with the level `OSLogType.fault`.
///
/// - Parameters:
/// - message: The main text message of the entry in the logs.
/// - details: Additional contextual data.
/// - message: A human-readable message, preferably generic and without interpolations. The `details` argument is for arguments.
/// - details: Structured and contextual details about a message.
/// - file: Implementations should have `#filePath` as the default value for this.
/// - function: Implementations should have `#function` as the default value for this.
/// - line: Implementations should have `#line` as the default value for this.
///
public func fault(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:]) {
public func fault(_ message: String, _ details: [FileProviderLogDetailKey: (any Sendable)?] = [:], file: StaticString = #filePath, function: StaticString = #function, line: UInt = #line) {
Task {
await log.write(category: category, level: .fault, message: message, details: details)
await log.write(category: category, level: .fault, message: message, details: details, file: file, function: function, line: line)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,14 @@ public protocol FileProviderLogging: Actor {
///
/// Usually, you do not need or want to use this but the methods provided by ``FileProviderLogger`` instead.
///
func write(category: String, level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?])
/// - Parameters:
/// - category: The unified logging category to use. Usually, this is the logging type.
/// - level: The severity of the message.
/// - message: A human-readable message, preferably generic and without interpolations. The `details` argument is for arguments.
/// - details: Structured and contextual details about a message.
/// - file: Implementations should have `#filePath` as the default value for this.
/// - function: Implementations should have `#function` as the default value for this.
/// - line: Implementations should have `#line` as the default value for this.
///
func write(category: String, level: OSLogType, message: String, details: [FileProviderLogDetailKey: (any Sendable)?], file: StaticString, function: StaticString, line: UInt)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public actor FileProviderLogMock: FileProviderLogging {
logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "FileProviderLogMock")
}

public func write(category _: String, level _: OSLogType, message: String, details _: [FileProviderLogDetailKey: (any Sendable)?]) {
public func write(category _: String, level _: OSLogType, message: String, details _: [FileProviderLogDetailKey: (any Sendable)?], file _: StaticString, function _: StaticString, line _: UInt) {
logger.debug("\(message, privacy: .public)")
}
}
Loading