You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
4.4 KiB

import Foundation
import os.log
/// iOS端的文件日志助手类,提供与Android端FileLogger类似的功能
class FileLogger {
private static let logQueue = DispatchQueue(label: "com.yunqiinnovation.classic_bluetooth.logger", qos: .background)
private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return formatter
}()
/// 日志类型
enum LogLevel: String {
case debug = "DEBUG"
case info = "INFO"
case warning = "WARNING"
case error = "ERROR"
}
/// 输出调试日志
/// - Parameters:
/// - tag: 标签
/// - message: 日志消息
static func d(_ tag: String, _ message: String) {
log(level: .debug, tag: tag, message: message)
}
/// 输出信息日志
/// - Parameters:
/// - tag: 标签
/// - message: 日志消息
static func i(_ tag: String, _ message: String) {
log(level: .info, tag: tag, message: message)
}
/// 输出警告日志
/// - Parameters:
/// - tag: 标签
/// - message: 日志消息
static func w(_ tag: String, _ message: String) {
log(level: .warning, tag: tag, message: message)
}
/// 输出错误日志
/// - Parameters:
/// - tag: 标签
/// - message: 日志消息
/// - error: 可选的错误对象
static func e(_ tag: String, _ message: String, _ error: Error? = nil) {
var fullMessage = message
if let error = error {
fullMessage += " - Error: \(error.localizedDescription)"
}
log(level: .error, tag: tag, message: fullMessage)
}
/// 输出日志
/// - Parameters:
/// - level: 日志级别
/// - tag: 标签
/// - message: 日志消息
private static func log(level: LogLevel, tag: String, message: String) {
let timestamp = dateFormatter.string(from: Date())
let logMessage = "[\(timestamp)] [\(level.rawValue)] [\(tag)]: \(message)"
// 输出到控制台
print(logMessage)
// 异步写入日志文件
logQueue.async {
writeToLogFile(logMessage)
}
// 使用系统日志
if #available(iOS 10.0, *) {
let osLog = OSLog(subsystem: "com.yunqiinnovation.classic_bluetooth", category: tag)
let osLogType: OSLogType
switch level {
case .debug:
osLogType = .debug
case .info:
osLogType = .info
case .warning:
osLogType = .default
case .error:
osLogType = .error
}
os_log("%{public}@", log: osLog, type: osLogType, message)
}
}
/// 将日志写入文件
/// - Parameter message: 日志消息
private static func writeToLogFile(_ message: String) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let logDirectoryURL = documentsDirectory.appendingPathComponent("Logs")
let dateString = DateFormatter().with { $0.dateFormat = "yyyy-MM-dd" }.string(from: Date())
let logFileURL = logDirectoryURL.appendingPathComponent("bluetooth_\(dateString).log")
do {
// 创建日志文件夹
try FileManager.default.createDirectory(at: logDirectoryURL, withIntermediateDirectories: true)
// 如果文件不存在,创建文件
if !FileManager.default.fileExists(atPath: logFileURL.path) {
FileManager.default.createFile(atPath: logFileURL.path, contents: nil)
}
// 追加日志内容
if let fileHandle = FileHandle(forWritingAtPath: logFileURL.path) {
fileHandle.seekToEndOfFile()
if let data = "\(message)\n".data(using: .utf8) {
fileHandle.write(data)
}
fileHandle.closeFile()
}
} catch {
print("写入日志文件失败: \(error.localizedDescription)")
}
}
}
// MARK: - DateFormatter 扩展
extension DateFormatter {
func with(_ block: (DateFormatter) -> Void) -> DateFormatter {
block(self)
return self
}
}