ios-hooks/AppRunMan/server/XSLog.m
2025-11-06 15:26:20 +08:00

163 lines
5.6 KiB
Objective-C

#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]);
});
}