如何NSLog到一个文件

是否有可能写每个NSLog不仅在控制台,而是在一个文件呢? 我想准备这个没有someExternalFunctionForLoggingreplacesomeExternalFunctionForLogging

取代所有的NSLog将是真正的问题。 也许有可能从控制台parsing数据或捕获消息?

选项1:使用ASL

NSLog输出日志到ASL(苹果公司的版本的系统日志)和控制台,这意味着当你使用iPhone模拟器时,它已经写入到Mac中的文件。 如果您想读取它,请打开应用程序Console.app,然后在filter字段中键入您的应用程序的名称。 要在您的iPhone设备中执行相同的操作,您需要使用ASL API并进行一些编码。

选项2:写入文件

比方说,你正在模拟器上运行,你不想使用Console.app。 您可以使用freopen将错误streamredirect到您喜欢的文件:
freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
有关详细信息,请参阅此说明和示例项目

或者你可以使用macros来自定义函数覆盖NSLog。 例如,将这个类添加到您的项目中:

 // file Log.h #define NSLog(args...) _Log(@"DEBUG ", __FILE__,__LINE__,__PRETTY_FUNCTION__,args); @interface Log : NSObject void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...); @end // file Log.m #import "Log.h" @implementation Log void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...) { va_list ap; va_start (ap, format); format = [format stringByAppendingString:@"\n"]; NSString *msg = [[NSString alloc] initWithFormat:[NSString stringWithFormat:@"%@",format] arguments:ap]; va_end (ap); fprintf(stderr,"%s%50s:%3d - %s",[prefix UTF8String], funcName, lineNumber, [msg UTF8String]); [msg release]; } @end 

并导入项目范围广泛添加到您的<application>-Prefix.pch

 #import "Log.h" 

现在,每次调用NSLog都会被自定义函数replace,而不需要触摸现有的代码。 但是,上面的function只是打印到控制台。 要添加文件输出,请在_Log上面添加这个函数:

 void append(NSString *msg){ // get path to Documents/somefile.txt NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *path = [documentsDirectory stringByAppendingPathComponent:@"logfile.txt"]; // create if needed if (![[NSFileManager defaultManager] fileExistsAtPath:path]){ fprintf(stderr,"Creating file at %s",[path UTF8String]); [[NSData data] writeToFile:path atomically:YES]; } // append NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path]; [handle truncateFileAtOffset:[handle seekToEndOfFile]]; [handle writeData:[msg dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; } 

并在_Log函数的fprintf下面添加这一行:

 append(msg); 

文件编写也可以在你的iPhone设备上运行,但是文件将被创build在其中的一个目录中,除非你添加代码将其发送回你的mac,或者在你的视图中显示它,否则你将无法访问应用程序,或使用iTunes来添加文档目录。

有一个更容易的方法。 这里是将NSLog输出redirect到应用程序的Documents文件夹中的文件的方法。 当你想在你的开发工作室之外testing你的应用程序时,这一点很有用,从你的Mac中拔出。

ObjC:

 - (void)redirectLogToDocuments { NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [allPaths objectAtIndex:0]; NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"yourFile.txt"]; freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr); } 

迅速:

 func redirectLogToDocuments() { let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsDirectory = allPaths.first! let pathForLog = documentsDirectory.stringByAppendingString("/yourFile.txt") freopen(pathForLog.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr) } 

执行这个方法后, NSLog (ObjC)或者print (Swift)生成的所有输出都会被转发到指定的文件。 要让保存的文件打开Organizer ,浏览应用程序的文件并将文件系统中的Application Data保存到Documents夹中,而不是简单地浏览到Documents夹。

我发现问题的最简单的解决scheme: login到iPhone上的文件 。 无需更改任何NSLog代码或更改logging器本身,只需将这4行添加到您的didFinishLaunchingWithOptions,并确保在您的生成设置,实时发布将不会激活(我为此添加了LOG2FILE标志)。

 #ifdef LOG2FILE #if TARGET_IPHONE_SIMULATOR == 0 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.log"]; freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr); #endif #endif 

把JaakL的答案翻译成了Swift ,在任何情况下,都可以在这里发布

在你的应用程序的某个地方运行这个代码,从那一刻起,它将所有NSLog()输出存储到文档目录中的一个文件中。

 let docDirectory: NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as NSString let logpath = docDirectory.stringByAppendingPathComponent("YourFileName.txt") freopen(logpath.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr) 

额外:如何用Xcode查找日志文件:
您可以简单地从Xcode访问日志:Windows>设备>select您的应用程序> InfoWheelButton>下载容器。 用finder查看文件:在文件上单击鼠标右键>显示包内容> appdata>文件>那里的文件是

好! 首先,我要感谢Evan-Mulawski。 这是我的解决scheme,也许对某个人有帮助:

在AppDelegate我添加function:

 void logThis(NSString* Msg, ...) { NSArray* findingMachine = [Msg componentsSeparatedByString:@"%"]; NSString* outputString = [NSString stringWithString:[findingMachine objectAtIndex:0]]; va_list argptr; va_start(argptr, Msg); for(int i = 1; i < [findingMachine count]; i++) { if ([[findingMachine objectAtIndex:i] hasPrefix:@"i"]||[[findingMachine objectAtIndex:i] hasPrefix:@"d"]) { int argument = va_arg(argptr, int); /* next Arg */ outputString = [outputString stringByAppendingFormat:@"%i", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"@"]) { id argument = va_arg(argptr, id); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%@", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"."]) { double argument = va_arg(argptr, double); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%f", argument]; NSRange range; range.location = 0; range.length = 3; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"f"]) { double argument = va_arg(argptr, double); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%f", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else { outputString = [outputString stringByAppendingString:@"%"]; outputString = [outputString stringByAppendingString:[findingMachine objectAtIndex:i]]; } } va_end(argptr); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); NSString * filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:@"logFile.txt"]; NSError* theError = nil; NSString * fileString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&theError]; if (theError != nil||[fileString length]==0) { fileString = [NSString stringWithString:@""]; } fileString = [fileString stringByAppendingFormat:@"\n%@",outputString]; if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&theError]) { NSLog(@"Loging problem"); } NSLog(@"%@",outputString); } 

然后,使用“全部replace”NSLog – > logThis。 此代码适用于我的应用程序。 它可以针对不同的需求进行扩展。


Thnks寻求帮助。

这是我使用和运作良好:

http://parmanoir.com/Redirecting_NSLog_to_a_file

希望能帮助到你。

为了内容的缘故,我只是在这里发布

 - (BOOL)redirectNSLog { // Create log file [@"" writeToFile:@"/NSLog.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; id fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/NSLog.txt"]; if (!fileHandle) return NSLog(@"Opening log failed"), NO; [fileHandle retain]; // Redirect stderr int err = dup2([fileHandle fileDescriptor], STDERR_FILENO); if (!err) return NSLog(@"Couldn't redirect stderr"), NO; return YES; } 

Swift 2.0:

将这些添加到Appdelegate didFinishLaunchWithOptions。

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { var paths: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsDirectory: String = paths[0] let logPath: String = documentsDirectory.stringByAppendingString("/console.log") if (isatty(STDERR_FILENO) == 0) { freopen(logPath, "a+", stderr) freopen(logPath, "a+", stdin) freopen(logPath, "a+", stdout) } print(logPath) return true } 

访问console.log:

当日志path打印在Xcode Log Area上时,selectpath,右键单击,在Finder中selectServices- Reaveal并打开文件console.log

我对阿尔文·乔治的回答做了一点点工作。

为了保持日志文件的大小,我实现了一个“十代日志文件”解决scheme(快而脏),并添加一个func来删除它们

每次应用程序启动时,它都会生成一个索引为“0”的新日志文件。 退出的文件将被重新命名为比以前更高的索引。 索引“10”将被删除。

所以,每一个开始给你一个新的日志文件,最多10代

可能不是最优雅的方式去做,但在过去的几个星期内对我来说非常好,因为我需要一些长时间的logging“closures”

  // ----------------------------------------------------------------------------------------------------------- // redirectConsoleToFile() // // does two things // 1) redirects "stderr", "stdin" and "stdout" to a logfile // 2) deals with old/existing files to keep up to 10 generations of the logfiles // tested with IOS 9.4 and Swift 2.2 func redirectConsoleToFile() { // Instance of a private filemanager let myFileManger = NSFileManager.defaultManager() // the path of the documnts directory of the app let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! // maximum number of logfiles let maxNumberOfLogFiles: Int = 10 // look if the max number of files already exist var logFilePath : String = documentDirectory.stringByAppendingString("/Console\(maxNumberOfLogFiles).log") var FlagOldFileNoProblem: Bool = true if myFileManger.fileExistsAtPath(logFilePath) == true { // yes, max number of files reached, so delete the oldest one do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile \(maxNumberOfLogFiles): \(error.description)") FlagOldFileNoProblem = false } } // test, if there was a problem with the old file if FlagOldFileNoProblem == true { // loop over all possible filenames for i in 0 ..< maxNumberOfLogFiles { // look, if an old file exists, if so, rename it with an index higher than before logFilePath = documentDirectory.stringByAppendingString("/Console\((maxNumberOfLogFiles - 1) - i).log") if myFileManger.fileExistsAtPath(logFilePath) == true { // there is an old file let logFilePathNew = documentDirectory.stringByAppendingString("/WayAndSeeConsole\(maxNumberOfLogFiles - i).log") do { // rename it try myFileManger.moveItemAtPath(logFilePath, toPath: logFilePathNew) } catch let error as NSError { // something went wrong print("ERROR renaming logFile: (i = \(i)), \(error.description)") FlagOldFileNoProblem = false } } } } // test, if there was a problem with the old files if FlagOldFileNoProblem == true { // No problem so far, so try to delete the old file logFilePath = documentDirectory.stringByAppendingString("/Console0.log") if myFileManger.fileExistsAtPath(logFilePath) == true { // yes, it exists, so delete it do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile 0: \(error.description)") } } } // even if there was a problem with the files so far, we redirect logFilePath = documentDirectory.stringByAppendingString("/Console0.log") if (isatty(STDIN_FILENO) == 0) { freopen(logFilePath, "a+", stderr) freopen(logFilePath, "a+", stdin) freopen(logFilePath, "a+", stdout) displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout redirected to \"\(logFilePath)\"") } else { displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout NOT redirected, STDIN_FILENO = \(STDIN_FILENO)") } } // ----------------------------------------------------------------------------------------------------------- // cleanupOldConsoleFiles() // // delete all old consolfiles func cleanupOldConsoleFiles() { // Instance of a private filemanager let myFileManger = NSFileManager.defaultManager() // the path of the documnts directory of the app let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! // maximum number of logfiles let maxNumberOfLogFiles: Int = 10 // working string var logFilePath: String = "" // loop over all possible filenames for i in 0 ... maxNumberOfLogFiles { // look, if an old file exists, if so, rename it with an index higher than before logFilePath = documentDirectory.stringByAppendingString("/Console\(i).log") if myFileManger.fileExistsAtPath(logFilePath) == true { // Yes, file exist, so delete it do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile \"\(i)\": \(error.description)") } } } }