#import "XSLog.h" static NSString *kLogFilePath = @"app.log"; static unsigned long long kMaxFileSize = 10 * 1024 * 1024; // 10 MB static NSFileHandle *logFileHandle; static dispatch_queue_t logQueue; static int logCount = 0; // 用于控制滚动检查频率 static const int kLogCheckFrequency = 100; // 每100条日志检查一次文件大小 // 内部函数声明 static void setupLogFileHandle(NSString *logFilePath); static void checkAndRollLogFile(NSString *logFilePath); static NSString *getLogFilePath(NSString *logFileName); void XSLogSetupLogWithFilePath(NSString *logFilePath, unsigned long long maxFileSize) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ kLogFilePath = logFilePath; kMaxFileSize = maxFileSize; logQueue = dispatch_queue_create("com.yourcompany.app.logQueue", DISPATCH_QUEUE_SERIAL); // 初始化文件句柄 setupLogFileHandle(logFilePath); }); } void XSLogSetupLogWithFileName(NSString *logFileName, unsigned long long maxFileSize) { kLogFilePath = getLogFilePath(logFileName); XSLogSetupLogWithFilePath(kLogFilePath,maxFileSize); } static NSString *getLogFilePath(NSString *logFileName) { // 获取Documents目录路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths firstObject]; NSString *logFilePath = [documentsDirectory stringByAppendingPathComponent:logFileName]; return logFilePath; } static void setupLogFileHandle(NSString *logFilePath) { // 检查文件大小,如果超过最大值则进行滚动 checkAndRollLogFile(logFilePath); // 关闭旧的句柄(如果存在) if (logFileHandle) { [logFileHandle closeFile]; logFileHandle = nil; } // 打开文件句柄 logFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; if (!logFileHandle) { // 如果文件不存在,则创建 [[NSFileManager defaultManager] createFileAtPath:logFilePath contents:nil attributes:nil]; logFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; } if (logFileHandle) { // 将文件句柄移动到文件末尾 [logFileHandle seekToEndOfFile]; } else { fprintf(stderr, "Error: Could not open log file for writing.\n"); } } static void checkAndRollLogFile(NSString *logFilePath) { NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:logFilePath]) { NSError *error = nil; NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:logFilePath error:&error]; if (fileAttributes) { unsigned long long fileSize = [fileAttributes fileSize]; if (fileSize > kMaxFileSize) { // 文件过大,进行滚动 NSString *backupFilePath = [logFilePath stringByAppendingString:@".bak"]; [fileManager removeItemAtPath:backupFilePath error:nil]; // 移除旧的备份文件 [fileManager moveItemAtPath:logFilePath toPath:backupFilePath error:&error]; // 将当前文件备份 if (error) { fprintf(stderr, "Error rolling log file: %s\n", error.localizedDescription.UTF8String); } // 创建新的空日志文件 [fileManager createFileAtPath:logFilePath contents:nil attributes:nil]; // 重新设置文件句柄 setupLogFileHandle(kLogFilePath); } } else { fprintf(stderr, "Error getting file attributes: %s\n", error.localizedDescription.UTF8String); } } } void XSLogRedirectNSLog(void) { if (logFileHandle) { int fd = [logFileHandle fileDescriptor]; if (fd != -1) { // 将标准错误输出重定向到日志文件 if (dup2(fd, STDERR_FILENO) == -1) { fprintf(stderr, "Error redirecting stderr to log file.\n"); } } else { fprintf(stderr, "Error: logFileHandle has an invalid file descriptor.\n"); } } else { fprintf(stderr, "Error: logFileHandle is not initialized. Cannot redirect stderr.\n"); } } void XSLogMessage(NSString *format, ...) { va_list args; va_start(args, format); NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; va_end(args); dispatch_async(logQueue, ^{ // 获取当前时间 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; NSString *timestamp = [dateFormatter stringFromDate:[NSDate date]]; // 格式化日志消息 NSString *logString = [NSString stringWithFormat:@"%@ %@\n", timestamp, message]; // 将日志写入文件 NSData *data = [logString dataUsingEncoding:NSUTF8StringEncoding]; if (logFileHandle) { @try { [logFileHandle writeData:data]; // 每次写入后,递增计数器 logCount++; // 每隔 kLogCheckFrequency 条日志检查一次文件大小 if (logCount >= kLogCheckFrequency) { logCount = 0; // 重置计数器 checkAndRollLogFile(kLogFilePath); } } @catch (NSException *exception) { fprintf(stderr, "Error writing to log file: %s\n", exception.reason.UTF8String); } } // 同时输出到控制台,以便调试 fprintf(stderr, "%s", [logString UTF8String]); }); }