是否可以将每个NSLog
写入控制台,但也可以写入文件?我想在不将NSLog
替换为someExternalFunctionForLogging
的情况下进行准备。
替换所有NSLog
将是真正的问题。也许有可能从控制台解析数据或捕获消息?
答案 0 :(得分:84)
NSLog将日志输出到ASL(Apple的syslog版本)和控制台,这意味着当您使用iPhone模拟器时,它已经写入Mac中的文件。如果要读取它,请打开应用程序Console.app,然后在过滤器字段中键入应用程序的名称。要在iPhone设备中执行相同操作,您需要使用ASL API并进行一些编码。
假设您正在模拟器上运行,并且您不想使用Console.app。您可以使用freopen将错误流重定向到您喜欢的文件:
freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
有关详细信息,请参阅此explanation and sample project。
或者您可以使用宏来使用自定义函数覆盖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的调用都将替换为您的自定义函数,而无需触及现有代码。但是,上面的功能只是打印到控制台。要添加文件输出,请在_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设备,但该文件将在其中的目录中创建,除非您添加代码将其发送回Mac或在其上显示,否则您将无法访问该文件。查看应用程序内部,或使用iTunes添加文档目录。
答案 1 :(得分:75)
有一种更容易的方法。以下是将 NSLog
输出重定向到应用程序的Documents
文件夹中的文件的方法。当您想要在开发工作室之外测试应用程序时,这非常有用,可以从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
文件夹。
答案 2 :(得分:7)
将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&gt;装置&gt;选择你的应用&gt; InfoWheelButton&gt;下载容器。
使用finder查看文件:在文件上单击鼠标右键&gt;显示包裹内容&gt; appdata&gt;文件&gt;那里的文件是
答案 3 :(得分:6)
我找到了解决问题的最简单方法:Logging to a file on the iPhone。无需更改任何NSLog代码或更改记录器本身,只需将这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
答案 4 :(得分:3)
确定!首先,我要感谢Evan-Mulawski。 这是我的解决方案,也许对某人有帮助:
在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);
}
然后使用“替换所有”NSLog - &gt; logThis。 此代码适用于我的应用程序。它可以根据不同的需求进行扩展。
寻求帮助。
答案 5 :(得分:3)
这是我使用和运作良好的方法:
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;
}
答案 6 :(得分:2)
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:
在Xcode Log Area上打印日志路径时,选择路径,右键单击,在Finder中选择Services- Reaveal并打开文件console.log
答案 7 :(得分:1)
Swift 4版本
let docDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let logpathe = docDirectory.appendingPathComponent("Logerr.txt")
freopen(logpathe.path.cString(using: .ascii)!, "a+", stderr)
let logpatho = docDirectory.appendingPathComponent("Logout.txt")
freopen(logpatho.path.cString(using: .ascii)!, "a+", stdout)
Swift print()
的输出将位于stdout
答案 8 :(得分:0)
我对阿尔文乔治的答案做了一点工作。
为了控制日志文件的大小,我实现了(快速和脏)一个&#34; 10代日志文件&#34;解决方案并添加一个func以便稍后删除它们
每次应用启动时,它都会生成一个带有索引&#34; 0&#34;的新日志文件。将使用比以前更高的索引重命名现有文件。索引&#34; 10&#34;将被删除。
因此,每次启动都会为您提供一个新的日志文件,最多10代
可能不是最优雅的方式,但在过去的几周里我的工作非常好,因为我需要一些长时间的记录&#34;关闭mac&#34;
// -----------------------------------------------------------------------------------------------------------
// 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)")
}
}
}
}