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.
286 lines
9.0 KiB
286 lines
9.0 KiB
import Foundation
|
|
import os.log
|
|
|
|
/**
|
|
* 文件日志工具
|
|
* 将日志同时输出到控制台和文件系统
|
|
*/
|
|
@objc public class FileLogger: NSObject {
|
|
|
|
private static let TAG = "FileLogger"
|
|
private static var instance: FileLogger?
|
|
|
|
private let logDir: URL
|
|
private var currentLogFile: URL?
|
|
private let logQueue = DispatchQueue(label: "com.yunqiinnovation.deepvoice_utils.logger", qos: .utility)
|
|
|
|
private let dateFormatter: DateFormatter = {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "yyyy-MM-dd"
|
|
return formatter
|
|
}()
|
|
|
|
private let timeFormatter: DateFormatter = {
|
|
let formatter = DateFormatter()
|
|
formatter.dateFormat = "HH:mm:ss.SSS"
|
|
return formatter
|
|
}()
|
|
|
|
/**
|
|
* 初始化日志系统
|
|
*/
|
|
private init(directory: URL) {
|
|
self.logDir = directory.appendingPathComponent("logs", isDirectory: true)
|
|
super.init()
|
|
|
|
// 创建日志目录
|
|
try? FileManager.default.createDirectory(at: logDir, withIntermediateDirectories: true)
|
|
|
|
// 创建当天日志文件
|
|
updateLogFile()
|
|
|
|
// 记录初始化完成
|
|
self.d(FileLogger.TAG, "文件日志系统已初始化")
|
|
}
|
|
|
|
/**
|
|
* 更新日志文件为当天日期
|
|
*/
|
|
private func updateLogFile() {
|
|
let today = dateFormatter.string(from: Date())
|
|
let logFile = logDir.appendingPathComponent("log_\(today).txt")
|
|
|
|
if currentLogFile == nil || currentLogFile?.path != logFile.path {
|
|
currentLogFile = logFile
|
|
|
|
// 如果是新文件,添加文件头
|
|
if !FileManager.default.fileExists(atPath: logFile.path) {
|
|
let headerDate = DateFormatter()
|
|
headerDate.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
|
let headerLine = "===== 日志开始: \(headerDate.string(from: Date())) =====\n"
|
|
|
|
try? headerLine.write(to: logFile, atomically: true, encoding: .utf8)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 将日志写入文件
|
|
*/
|
|
private func logToFile(level: String, tag: String, message: String) {
|
|
logQueue.async { [weak self] in
|
|
guard let self = self else { return }
|
|
|
|
do {
|
|
// 检查是否需要更新日志文件
|
|
self.updateLogFile()
|
|
|
|
guard let logFile = self.currentLogFile,
|
|
FileManager.default.fileExists(atPath: logFile.path) else {
|
|
return
|
|
}
|
|
|
|
// 格式化日志内容
|
|
let timestamp = self.timeFormatter.string(from: Date())
|
|
let logLine = "\(timestamp) \(level)/\(tag): \(message)\n"
|
|
|
|
// 获取文件句柄并追加内容
|
|
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
|
|
fileHandle.seekToEndOfFile()
|
|
if let data = logLine.data(using: .utf8) {
|
|
fileHandle.write(data)
|
|
}
|
|
fileHandle.closeFile()
|
|
} else {
|
|
// 如果无法获取文件句柄,尝试直接写入
|
|
let existingContent = try? String(contentsOf: logFile, encoding: .utf8)
|
|
let newContent = (existingContent ?? "") + logLine
|
|
try newContent.write(to: logFile, atomically: true, encoding: .utf8)
|
|
}
|
|
} catch {
|
|
print("写入日志文件失败: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 调试级别日志
|
|
*/
|
|
private func d(_ tag: String, _ message: String) {
|
|
// 输出到控制台
|
|
print("D/\(tag): \(message)")
|
|
|
|
// 写入文件
|
|
logToFile(level: "D", tag: tag, message: message)
|
|
}
|
|
|
|
/**
|
|
* 信息级别日志
|
|
*/
|
|
private func i(_ tag: String, _ message: String) {
|
|
// 输出到控制台
|
|
print("I/\(tag): \(message)")
|
|
|
|
// 写入文件
|
|
logToFile(level: "I", tag: tag, message: message)
|
|
}
|
|
|
|
/**
|
|
* 警告级别日志
|
|
*/
|
|
private func w(_ tag: String, _ message: String) {
|
|
// 输出到控制台
|
|
print("W/\(tag): \(message)")
|
|
|
|
// 写入文件
|
|
logToFile(level: "W", tag: tag, message: message)
|
|
}
|
|
|
|
/**
|
|
* 错误级别日志
|
|
*/
|
|
private func e(_ tag: String, _ message: String, _ error: Error? = nil) {
|
|
var fullMessage = message
|
|
|
|
// 如果有错误,添加错误信息
|
|
if let error = error {
|
|
fullMessage += "\n\(error.localizedDescription)"
|
|
}
|
|
|
|
// 输出到控制台
|
|
print("E/\(tag): \(fullMessage)")
|
|
|
|
// 写入文件
|
|
logToFile(level: "E", tag: tag, message: fullMessage)
|
|
}
|
|
|
|
/**
|
|
* 清理旧日志文件
|
|
*/
|
|
private func cleanOldLogs(daysToKeep: Int = 7) {
|
|
logQueue.async {
|
|
do {
|
|
let currentDate = Date()
|
|
let fileManager = FileManager.default
|
|
|
|
let logFiles = try fileManager.contentsOfDirectory(at: self.logDir, includingPropertiesForKeys: [.creationDateKey], options: [])
|
|
|
|
for fileURL in logFiles where fileURL.pathExtension == "txt" {
|
|
if let fileAttributes = try? fileManager.attributesOfItem(atPath: fileURL.path),
|
|
let creationDate = fileAttributes[.creationDate] as? Date {
|
|
|
|
let age = Calendar.current.dateComponents([.day], from: creationDate, to: currentDate)
|
|
|
|
if let days = age.day, days > daysToKeep {
|
|
try? fileManager.removeItem(at: fileURL)
|
|
print("D/\(FileLogger.TAG): 已删除旧日志文件: \(fileURL.lastPathComponent)")
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
print("E/\(FileLogger.TAG): 清理旧日志失败: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - 公共静态方法
|
|
|
|
/**
|
|
* 初始化日志系统
|
|
*/
|
|
@objc public static func initialize() {
|
|
if instance == nil {
|
|
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
instance = FileLogger(directory: documentsDirectory)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化日志系统(使用自定义目录)
|
|
*/
|
|
@objc public static func initialize(withDirectory directory: URL) {
|
|
if instance == nil {
|
|
instance = FileLogger(directory: directory)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 调试日志
|
|
*/
|
|
@objc public static func d(_ tag: String, _ message: String) {
|
|
// 如果实例不存在,先使用默认目录初始化
|
|
if instance == nil {
|
|
initialize()
|
|
}
|
|
|
|
instance?.d(tag, message)
|
|
|
|
// 使用系统日志
|
|
if #available(iOS 10.0, *) {
|
|
os_log("%{public}@", log: OSLog(subsystem: "com.yunqiinnovation.deepvoice_utils", category: tag), type: .debug, message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 信息日志
|
|
*/
|
|
@objc public static func i(_ tag: String, _ message: String) {
|
|
if instance == nil {
|
|
initialize()
|
|
}
|
|
|
|
instance?.i(tag, message)
|
|
|
|
// 使用系统日志
|
|
if #available(iOS 10.0, *) {
|
|
os_log("%{public}@", log: OSLog(subsystem: "com.yunqiinnovation.deepvoice_utils", category: tag), type: .info, message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 警告日志
|
|
*/
|
|
@objc public static func w(_ tag: String, _ message: String) {
|
|
if instance == nil {
|
|
initialize()
|
|
}
|
|
|
|
instance?.w(tag, message)
|
|
|
|
// 使用系统日志
|
|
if #available(iOS 10.0, *) {
|
|
os_log("%{public}@", log: OSLog(subsystem: "com.yunqiinnovation.deepvoice_utils", category: tag), type: .default, message)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 错误日志
|
|
*/
|
|
@objc public static func e(_ tag: String, _ message: String, _ error: Error? = nil) {
|
|
if instance == nil {
|
|
initialize()
|
|
}
|
|
|
|
instance?.e(tag, message, error)
|
|
|
|
// 使用系统日志
|
|
if #available(iOS 10.0, *) {
|
|
let errorMessage = error != nil ? "\(message): \(error!.localizedDescription)" : message
|
|
os_log("%{public}@", log: OSLog(subsystem: "com.yunqiinnovation.deepvoice_utils", category: tag), type: .error, errorMessage)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清理旧日志
|
|
*/
|
|
@objc public static func cleanOldLogs(daysToKeep: Int = 7) {
|
|
instance?.cleanOldLogs(daysToKeep: daysToKeep)
|
|
}
|
|
|
|
/**
|
|
* 关闭日志系统
|
|
*/
|
|
@objc public static func shutdown() {
|
|
instance = nil
|
|
}
|
|
}
|